← Back

PROTOTYPE AND PROTOTYPE CHAIN IN JAVASCRIPT

This note explains prototype and prototype chains in simple language.

Prototype is one of the most important ideas in JavaScript. At first it looks confusing, but the basic idea is actually simple:

An object can use not only its own properties, but also properties from another object connected to it. That connection is called a prototype.

1. What is a prototype?

The connection between objects is provided by a special internal property called [[Prototype]].

We do not usually change it directly. JavaScript uses it automatically when it looks for a property in an object.

Simple idea

If JavaScript does not find a property inside the current object, it checks the prototype object.

So the prototype works like a backup place for properties and methods.

Diagram 1. Main idea

Object
│
├─ own properties
│
└─ [[Prototype]]
   └─ backup place for more properties

2. Creating an object with a prototype

To create a new object linked to another object, JavaScript uses:

Object.create(obj)

This method creates a new object and sets obj as its prototype.

Example

const animal = {
  legs: 4,
};

const dog = Object.create(animal);
dog.name = "Aaron";

console.log(dog); // { name: "Aaron", [[Prototype]]: animal }

Explanation

Here:

Diagram 2. dog and animal

animal
│
└─ legs: 4

dog
│
├─ name: "Aaron"
└─ [[Prototype]] → animal

3. How property lookup works

Look at this example:

const animal = {
  legs: 4,
};

const dog = Object.create(animal);
dog.name = "Aaron";

console.log(dog.name); // "Aaron"
console.log(dog.legs); // 4

What happens?

dog.name

JavaScript looks inside dog and finds:

name: "Aaron"

So it returns "Aaron".

dog.legs

JavaScript looks inside dog, but dog does not have its own legs property.

Then JavaScript checks dog[[Prototype]], which points to animal.

Inside animal, it finds:

legs: 4

So it returns 4.

Diagram 3. Property lookup

dog.legs
↓
look inside dog
↓
not found
↓
go to dog[[Prototype]]
↓
animal.legs
↓
4

4. Checking whether one object is the prototype of another

If you want to check whether one object is the prototype of another, use:

objA.isPrototypeOf(objB)

This checks whether objA is the prototype of objB.

Example

const customer = {
  username: "Jacob",
};

const animal = {
  legs: 4,
};

const dog = Object.create(animal);
dog.name = "Aaron";

console.log(animal.isPrototypeOf(dog)); // true
console.log(dog.isPrototypeOf(animal)); // false
console.log(customer.isPrototypeOf(dog)); // false

Explanation

Diagram 4. Prototype direction

animal → prototype of dog
dog    → not prototype of animal

5. Own properties and inherited properties

A property can be:

Example

const animal = {
  legs: 4,
};

const dog = Object.create(animal);
dog.name = "Aaron";

console.log(dog.name); // "Aaron"
console.log(dog.legs); // 4

Explanation

Diagram 5. Own vs inherited

dog
│
├─ own property:
│  └─ name: "Aaron"
│
└─ inherited property from prototype:
   └─ legs: 4

6. Checking own properties with hasOwnProperty()

To check whether an object has its own property, use:

obj.hasOwnProperty(key)

It returns:

Example

const animal = {
  legs: 4,
};

const dog = Object.create(animal);
dog.name = "Aaron";

console.log(dog.hasOwnProperty("name")); // true
console.log(dog.hasOwnProperty("legs")); // false

Explanation

Diagram 6. hasOwnProperty()

dog.hasOwnProperty("name") → true
dog.hasOwnProperty("legs") → false

7. Iterating over properties and the for...in problem

The for...in loop goes through both:

This is often inconvenient, because usually we want only own properties.

Example

const animal = { legs: 4 };
const dog = Object.create(animal);
dog.name = "Aaron";

for (const key in dog) {
  console.log(key); // "name" "legs"
}

Explanation

for...in prints both:

Diagram 7. for...in behavior

for...in over dog
↓
"name"
"legs"

8. How to iterate only over own properties

If you use for...in, add a check with hasOwnProperty().

Example

const animal = { legs: 4 };
const dog = Object.create(animal);
dog.name = "Aaron";

for (const key in dog) {
  if (dog.hasOwnProperty(key)) {
    console.log(key); // "name"
  }
}

Now only the property that belongs directly to dog is printed.

Diagram 8. Filtering only own properties

for...in
↓
check hasOwnProperty(key)
│
├─ true  → use it
└─ false → skip it

9. Better way: Object.keys() and Object.values()

In practice, developers usually use:

These return only own keys or only own values. They do not include inherited properties.

Example

const animal = { legs: 4 };
const dog = Object.create(animal);
dog.name = "Aaron";

console.log(Object.keys(dog)); // ["name"]
console.log(Object.values(dog)); // ["Aaron"]

for (const key of Object.keys(dog)) {
  console.log(key); // "name"
}

This is why Object.keys() with for...of is often preferred over for...in.

Diagram 9. Better iteration

Object.keys(dog)
↓
["name"]

Object.values(dog)
↓
["Aaron"]

10. Prototype chains

A prototype can also have its own prototype.

That means JavaScript can build a prototype chain.

Example

const objC = { c: "objC prop" };

const objB = Object.create(objC);
objB.b = "objB prop";

const objA = Object.create(objB);
objA.a = "objA prop";

console.log(objA); // { a: "objA prop", [[Prototype]]: objB }
console.log(objB); // { b: "objB prop", [[Prototype]]: objC }
console.log(objC); // { c: "objC prop", [[Prototype]]: Object }

Explanation

Here we have a chain:

objA → objB → objC → Object

So:

Diagram 10. Prototype chain

objA
│
├─ a: "objA prop"
└─ [[Prototype]] → objB
                    │
                    ├─ b: "objB prop"
                    └─ [[Prototype]] → objC
                                        │
                                        ├─ c: "objC prop"
                                        └─ [[Prototype]] → Object

11. Access through the prototype chain

Example

const objC = { c: "objC prop" };

const objB = Object.create(objC);
objB.b = "objB prop";

const objA = Object.create(objB);
objA.a = "objA prop";

console.log(objA.hasOwnProperty("a")); // true
console.log(objA.a); // "objA prop"

console.log(objA.hasOwnProperty("b")); // false
console.log(objA.b); // "objB prop"

console.log(objA.hasOwnProperty("c")); // false
console.log(objA.c); // "objC prop"

console.log(objA.hasOwnProperty("x")); // false
console.log(objA.x); // undefined

Explanation

JavaScript searches in this order:

  1. objA
  2. objB
  3. objC
  4. Object
  5. if still not found → undefined

Diagram 11. Property search path

objA.x
↓
check objA
↓
check objB
↓
check objC
↓
check Object
↓
not found
↓
undefined

12. End of the prototype chain

At the end of the prototype chain there is always a reference to the base object class.

For plain objects, the chain ends at Object.

Example

const objB = {
  b: "objB prop",
};

const objA = Object.create(objB);
objA.a = "objA prop";

console.log(objA);

If you expand this in the browser developer console, you can see that:

Diagram 12. End of the chain

objA
↓
objB
↓
Object
↓
end of chain

13. Easy memory rules

Prototype = backup place for properties
Own property = belongs directly to the object
Inherited property = comes from prototype
Prototype chain = object → prototype → prototype of prototype
End of chain = Object

14. Quick summary

15. Final conclusion

Prototype is the mechanism that lets one object use properties from another object.

If you understand:

then you already understand one of the most important parts of JavaScript objects.

← Back